﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Xml.Linq;
using System.IO;

namespace ZetaPrints.ZetaClient.Data
{
    public class ZetaClientConfiguration
    {
        #region public methods
        /// <summary>
        /// Gets the current location of the application on 
        /// the windows directory system
        /// </summary>
        public static string CurrentApplicationLocation
        {
            get
            {
                /// get the directory we are running from
                string currentDirectory = Assembly.GetExecutingAssembly().Location;

                /// remove the file name
                currentDirectory = currentDirectory.Substring(0, currentDirectory.LastIndexOf('\\'));

                return currentDirectory;
            }
        }
        /// <summary>
        /// If we should download the order details when the order is downloaded
        /// </summary>
        public bool DownloadOrderDetails { get; set; }
        /// <summary>
        /// If we should download files when we dowload the order details
        /// </summary>
        public bool DownloadFiles { get; set; }
        /// <summary>
        /// A list of file extensios separated by commas. This extensios are
        /// the file extensions of files that will be downloaded and saved
        /// automatically
        /// </summary>
        public string DownloadFileExtensions { get; set; }
        /// <summary>
        /// The root directory of all the download
        /// </summary>
        public string DownloadRootDirectory { get; set; }
        /// <summary>
        /// The location of the database file
        /// </summary>
        public string DataSource { get; set; }
        /// <summary>
        /// Gets only the file location of the DataSource string
        /// </summary>
        public string DataSourceFile
        {
            get
            {
                return DataSource.Substring(DataSource.IndexOf("=") + 1);
            }
        }
        /// <summary>
        /// Is the datasource path and file name valid?
        /// </summary>
        public bool IsDataSourceValid
        {
            get
            {
                bool isValid = false;

                if (File.Exists(DataSourceFile))
                {
                    isValid = true;
                }

                return isValid;
            }
        }
        #endregion


        #region constructors
        /// <summary>
        /// Initializes the class and gets the values from the file
        /// </summary>
        public ZetaClientConfiguration()
        {
            Open();

            /// if the download directory does not exist create it.
            if (!Directory.Exists(DownloadRootDirectory))
            {
                Directory.CreateDirectory(DownloadRootDirectory);
            }
        }
        #endregion


        #region protected methods
        /// <summary>
        /// Creates Xml from the current class with 
        /// a specific name for the xml
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        protected virtual XElement ToNamedXml(string name)
        {
            XElement xElement = new XElement(name);

            List<PropertyInfo> propertyList = new List<PropertyInfo>(GetType().GetProperties());

            foreach (PropertyInfo propertyItem in propertyList)
            {
                if (propertyItem.PropertyType.IsSealed)
                {
                    xElement.Add(new XAttribute(propertyItem.Name, propertyItem.GetValue(this, null).ToString()));
                }
                else
                {
                    Type anotherClass = propertyItem.PropertyType;

                    List<string> parameterList = new List<string> { propertyItem.Name };

                    MethodInfo methodToInvoke = anotherClass.GetMethod("ToNamedXml");
                    if (methodToInvoke != null)
                    {
                        xElement.Add(methodToInvoke.Invoke(propertyItem.GetValue(this, null), parameterList.ToArray()));
                    }
                }
            }

            return xElement;
        }
        /// <summary>
        /// Parses the xml file into the property it has in this system
        /// </summary>
        /// <param name="xElement"></param>
        /// <returns></returns>
        public virtual bool ParseNamedXml(string name, XElement xElement)
        {
            bool dataParse = false;

            if (name == xElement.Name)
            {
                dataParse = true;

                List<PropertyInfo> propertyList = new List<PropertyInfo>(GetType().GetProperties());

                /// go through each property and match it with the Xml data
                foreach (PropertyInfo propertyItem in propertyList)
                {
                    try
                    {
                        if (propertyItem.PropertyType.IsSealed)
                        {
                            if (xElement.Attribute(propertyItem.Name) != null)
                            {
                                if (propertyItem.Name == "CurrentApplicationLocation")
                                {
                                    /// we do not do anything if this is the case,
                                    /// we are not saving this item
                                }
                                else if (propertyItem.PropertyType.FullName == "System.Boolean")
                                {
                                    /// get the simple value up to the system
                                    propertyItem.SetValue(this, Convert.ToBoolean(xElement.Attribute(propertyItem.Name).Value), null);
                                }
                                else
                                {
                                    /// get the simple value up to the system
                                    propertyItem.SetValue(this, xElement.Attribute(propertyItem.Name).Value, null);
                                }
                            }
                        }
                        else
                        {
                            Type anotherClass = propertyItem.PropertyType;

                            MethodInfo methodToInvoke = anotherClass.GetMethod("ParseXml");
                            if (methodToInvoke != null)
                            {
                                /// create the list of parameters
                                List<object> parameterList = new List<object> { xElement.Element(propertyItem.Name) };

                                /// invoke the ParseXml method
                                methodToInvoke.Invoke(propertyItem.GetValue(this, null), parameterList.ToArray());
                            }
                        }
                    }
                    catch (ArgumentException ex)
                    {
                        if (ex.Message.Contains("Property set method not found"))
                        {
                            /// do nothing
                        }
                        else
                        {
                            throw;
                        }
                    }
                }
            }

            return dataParse;
        }
        #endregion


        #region public methods
        #region xml
        public virtual XElement ToXml()
        {
            return ToNamedXml("Configuration");
        }
        public virtual bool ParseXml(XElement xElement)
        {
            return ParseNamedXml("Configuration", xElement);
        }
        #endregion
        #region file access
        /// <summary>
        /// Saves the current configuration
        /// </summary>
        /// <returns></returns>
        public virtual bool Save()
        {
            bool fileSaved = false;

            using (System.IO.StreamWriter sw = new System.IO.StreamWriter(@"Configuration.xml", false))
            {
                sw.WriteLine(ToXml());
                fileSaved = true;
            }

            return fileSaved;
        }
        /// <summary>
        /// Opens the file and loads the values into this class
        /// </summary>
        /// <returns></returns>
        public virtual bool Open()
        {
            bool fileOpenned = false;

            string configurationFileLocation = ZetaClientConfiguration.CurrentApplicationLocation + @"\Configuration.xml";


            if (System.IO.File.Exists(configurationFileLocation))
            {
                string fileContents;
                using (System.IO.StreamReader sr = new System.IO.StreamReader(configurationFileLocation))
                {
                    fileContents = sr.ReadToEnd();
                }

                if (ParseXml(XElement.Parse(fileContents)))
                {
                    fileOpenned = true;
                }
            }
            
            /// if the file was not found we need to add a default
            if(!fileOpenned)
            {
                fileOpenned = true;

                DownloadOrderDetails = false;
                DownloadFiles = false;
                DownloadFileExtensions = string.Empty;
                DownloadRootDirectory = "Files";
                SetDefaultDataSource();

                Save();
            }
            

            return fileOpenned;
        }
        #endregion
        #region special tools
        /// <summary>
        /// Will attempt to auto recover the datasource by reseting back to
        /// default data source, if it can it will keep the new data source,
        /// if not the default DataSource will be kept and the method will
        /// return false
        /// </summary>
        /// <returns></returns>
        public bool AutoDataSourceRecover()
        {
            bool autoDataSourceRecoverWorked = false;

            /// save the original data source
            string originalDataSource = DataSource;

            /// set back to default
            SetDefaultDataSource();

            /// are we valid now?
            if (IsDataSourceValid)
            {
                autoDataSourceRecoverWorked = true;
                Save();
            }
            else
            {
                DataSource = originalDataSource;
            }

            return autoDataSourceRecoverWorked;
        }
        /// <summary>
        /// This call will set the entry as the default it was intended
        /// </summary>
        public void SetDefaultDataSource()
        {
            /// get the directory we are running from
            string currentDirectory = Assembly.GetExecutingAssembly().Location;

            /// remove the file name
            currentDirectory = currentDirectory.Substring(0, currentDirectory.LastIndexOf('\\'));

            /// sets it back to default
            DataSource = @"Data Source=ZetaClientData.sdf";
        }
        /// <summary>
        /// Removes the DownloadRootDirectory path from a file path,
        /// if one is found.
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public string NormalizeFileWithDownloadRootDirectory(string filePath)
        {
            string normalizeFile = filePath;

            /// the entry from the configration system 
            ZetaClientConfiguration zetaClientConfiguration = new ZetaClientConfiguration();
            
            string downloadRootDirectory = zetaClientConfiguration.DownloadRootDirectory;

            /// does the download directory is a specification location? Or is
            /// based of our current location?
            if (!downloadRootDirectory.Contains(":") &&
                !downloadRootDirectory.Contains(@"\\"))
            {
                /// get the corret name now
                downloadRootDirectory = ZetaClientConfiguration.CurrentApplicationLocation + @"\" + downloadRootDirectory;

                /// if this contains a relative path we need to fix it.
                if (filePath.Contains(downloadRootDirectory))
                {
                    normalizeFile = filePath.Replace(ZetaClientConfiguration.CurrentApplicationLocation, string.Empty);

                    /// remove the initial directory entry from
                    /// the start of the file path.
                    normalizeFile = normalizeFile.TrimStart('\\');
                }
            }

            return normalizeFile;
        }
        #endregion
        #endregion
    }
}
